//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.DataLayer
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class ActivityAfterCalls : IDataLayerSecurityAfterCalls
    {
        private Connection conn;

        /// <summary>
        /// Initializes a new instance of the <see cref="AfterCalls"/> class.
        /// </summary>
        /// <param name="connection">The connection.</param>
        public ActivityAfterCalls(Connection connection)
        {
            this.conn = connection;
        }

        #region IDataLayerSecurityAfterCalls Members
        public SecurityAction Object(out Exception error, ref Connection.Storage storage, ref Connection.Action action,
            ref List<Parameter> paramListReq, ref List<Parameter> paramListResp)
        {
            return CheckActivity(storage, action, paramListResp, out error);
        }

        public SecurityAction Object(out Exception error, ref Connection.Storage storage, ref Connection.Action action,
            Connection.ObjectCreator creator, ref List<IObject> results)
        {
            return FilterActivities(action, storage, ref results, out error);
        }

        public SecurityAction Relation(out Exception error, ref Connection.Action action,
            ref IObject obj1, ref IObject obj2)
        {
            error = null;

            if (obj2 is Activity)
            {
                Activity checkActivity = obj2 as Activity;
                if (checkActivity != null && checkActivity.IsWorkflow)
                {
                    return CheckAccess(ActivitySecurity.GetUser(this.conn), checkActivity) ? SecurityAction.Allow : SecurityAction.Deny;
                }
            }

            return SecurityAction.Allow;
        }

        public SecurityAction Relation(out Exception error, ref Connection.Action action,
            ref IObject obj1, ref Connection.Storage obj2, ref string revKey, ref List<Parameter> paramListReq,
            ref List<Parameter> paramListResp)
        {
            return CheckActivity(obj2, action, paramListResp, out error);
        }

        public SecurityAction Relation(out Exception error, ref Connection.Action action,
            ref IObject obj1, ref Connection.Storage obj2Type, Connection.ObjectCreator creator, ref List<IObject> results)
        {
            return FilterActivities(action, obj2Type, ref results, out error);
        }

        public SecurityAction Relation(out Exception error, ref Connection.Action action,
            ref IObject obj1, ref string field, ref Connection.Storage obj2Type, Connection.ObjectCreator creator,
            ref List<IObject> results)
        {
            return FilterActivities(action, obj2Type, ref results, out error);
        }

        #endregion

        #region Private methods

        private SecurityAction FilterActivities(Connection.Action action, Connection.Storage obj2Type,
            ref List<IObject> results, out Exception error)
        {
            error = null;

            if (obj2Type.Name.Equals("Activity", StringComparison.OrdinalIgnoreCase))
            {
                switch (action)
                {
                    case Connection.Action.GetAll:
                    case Connection.Action.GetByObject:
                    case Connection.Action.Search:
                        {
                            results = GetActivities(results, ActivitySecurity.GetUser(this.conn));
                            break;
                        }
                }
            }

            return SecurityAction.Allow;
        }

        private SecurityAction CheckActivity(Connection.Storage storage, Connection.Action action,
            List<Parameter> paramListResp, out Exception error)
        {
            error = null;

            if (storage.Name.Equals("Activity", StringComparison.OrdinalIgnoreCase))
            {
                Activity checkActivity;

                switch (action)
                {
                    case Connection.Action.Get:
                    case Connection.Action.GetByID:
                    case Connection.Action.GetByObject:
                        {
                            checkActivity = GetActivity(paramListResp);
                            if (checkActivity != null && checkActivity.IsWorkflow)
                            {
                                return CheckAccess(ActivitySecurity.GetUser(this.conn), checkActivity) ? SecurityAction.Allow : SecurityAction.Deny;
                            }
                            break;
                        }
                }
            }

            return SecurityAction.Allow;
        }

        /// <summary>
        /// Gets the activities.
        /// </summary>
        /// <param name="results">The results.</param>
        /// <param name="currentUser">The current user.</param>
        /// <returns></returns>
        private static List<IObject> GetActivities(List<IObject> results, User currentUser)
        {
            List<IObject> actualResults = new List<IObject>();
            Activity checkActivity;
            foreach (IObject resultActivity in results)
            {
                checkActivity = resultActivity as Activity;
                if (checkActivity != null && checkActivity.IsWorkflow)
                {
                    if (CheckAccess(currentUser, checkActivity))
                    {
                        actualResults.Add(checkActivity);
                    }
                }
                else
                {
                    actualResults.Add(checkActivity);
                }
            }

            return actualResults;
        }

        /// <summary>
        /// Checks the access.
        /// </summary>
        /// <param name="currentUser">The current user.</param>
        /// <param name="checkActivity">The check activity.</param>
        /// <returns></returns>
        private static bool CheckAccess(User currentUser, Activity checkActivity)
        {
            return checkActivity != null &&
                    (checkActivity.Security.Rights.AuthorWorkflow.Check(currentUser) ||
                    checkActivity.Security.Rights.RunWorkflow.Check(currentUser));
        }

        /// <summary>
        /// Gets the activity.
        /// </summary>
        /// <param name="paramList">The param list.</param>
        /// <returns></returns>
        private static Activity GetActivity(List<Parameter> paramList)
        {
            Activity checkActivity = null;
            foreach (Parameter p in paramList)
            {
                if (p.Direction == Parameter.ParamDirection.Result)
                {
                    checkActivity = p.Value as Activity;
                    if (checkActivity != null)
                        break;
                }
                else if (p is IPipelineParameter)
                {
                    IPipelineParameter param = p as IPipelineParameter;
                    checkActivity = param.Value as Activity;
                    if (checkActivity != null)
                        break;
                }
            }
            return checkActivity;
        }

        #endregion
    }
}
